// Copyright DustedPixels.com 2008. All rights reserved.

package com.dustedpixels.asm.utils;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import com.dustedpixels.asm.MethodAdapter;


/**
 * @author micapolos@gmail.com (Michal Pociecha-Los)
 */
public final class LocalsRemapper extends MethodAdapter {
  /**
   * Returns new instance of {@code MethodVisitor} which will remap all access
   * to local variables by shifting it by given value, and writes the result
   * into underlying output.
   * 
   * @param out remapper output
   * @param shift shift value
   * @return shifting adapter
   */
  public static MethodVisitor shift(MethodVisitor out, final int shift) {
    return new LocalsRemapper(out, new RemapFunction() {
      public int apply(int local) {
        return local + shift;
      }
    });
  }

  // Remap function
  private final RemapFunction remapFunction;
  
  /**
   * Returns loca....
   * @param out
   * @param remapFunction
   */
  private LocalsRemapper(MethodVisitor out, RemapFunction remapFunction) {
    super(out);
    
    this.remapFunction = remapFunction;
  }
    
  @Override
  public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
    throw new RuntimeException("Not supported");
  }

  @Override
  public void visitIincInsn(int var, int increment) {
    super.visitIincInsn(remapFunction.apply(var), increment);
  }

  @Override
  public void visitLocalVariable(String name, String desc, String signature, Label start,
      Label end, int index) {
    super.visitLocalVariable(name, desc, signature, start, end, remapFunction.apply(index));
  }

  @Override
  public void visitVarInsn(int opcode, int var) {
    super.visitVarInsn(opcode, remapFunction.apply(var));
  }

  private interface RemapFunction {
    int apply(int local);
  }
}

